package com.qd.panda.gas.sync;


import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.URLUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.cdqidi.config.mybatis.handler.GeoPoint;
import com.cdqidi.constant.ConstantDto;
import com.cdqidi.exception.ApiException;
import com.cdqidi.util.FileUtil;
import com.cdqidi.util.SortUtil;
import com.cdqidi.util.SpringContextHolder;
import com.qd.common.panda.constant.PandaConstant;
import com.qd.common.panda.domain.entity.carpark.CarParkDTO;
import com.qd.common.panda.domain.enums.GasStationCompanyNameEnum;
import com.qd.common.panda.domain.enums.GasStationTypeEnum;
import com.qd.common.sys.constant.Constants;
import com.qd.common.sys.domain.entity.log.SendLogDTO;
import com.qd.common.sys.event.SendLogEvent;
import com.qd.panda.gas.station.shell.last.LastShellHttpServiceImpl;
import com.qd.panda.gas.station.shell.resp.last.GasPriceDTO;
import com.qd.panda.gas.station.shell.resp.last.GasStationDTO;
import com.qd.panda.model.*;
import com.qd.panda.service.park.*;
import com.qd.panda.service.stroke.BigDecimalUtil;
import com.qd.upload.config.UploadDTO;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.URL;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 同步壳牌加油站的数据（四川）,重新启用
 * 同步普通加油站数据
 *
 * @author sjk
 * @create 2022-10-09 11:23
 * @since 1.0.0
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class LastShellSyncService {
    /**
     * 默认折扣97折
     */
    public static final Double DEFAULT_CUSTOMER_DISCOUNT = 97D;
    private final LastShellHttpServiceImpl lastShellHttpService;
    private final CarParkService carParkService;
    private final CarParkImgService carParkImgService;
    private final ParkHolderService parkHolderService;
    private final CarParkLocationService carParkLocationService;
    private final CarParkGasStationService carParkGasStationService;
    private final CarParkGasStationPriceService carParkGasStationPriceService;
    private final CarParkGasStationNozzleService carParkGasStationNozzleService;

    @Resource
    private UploadDTO uploadDTO;

    @Transactional(rollbackFor = Exception.class)
    @SneakyThrows
    public void sync() {
        SendLogDTO sendLogDTO = new SendLogDTO();
        try {
            try {
                sendLogDTO.setSendState(ConstantDto.SF1);
                sendLogDTO.setLogType(PandaConstant.LOG_TYPE_GAS_STATIONS);
                sendLogDTO.setAddTime(LocalDateTime.now());
                sync(sendLogDTO);
            } finally {
                SpringContextHolder.publishEvent(new SendLogEvent(sendLogDTO));
            }
        } catch (Exception e) {
            final String message = ExceptionUtil.getMessage(e);
            sendLogDTO.setErrMsg(message);
            sendLogDTO.setSendState(ConstantDto.SF0);
            throw e;
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @SneakyThrows
    public void sync(SendLogDTO sendLogDTO) {
        long start = System.currentTimeMillis();
        List<GasStationDTO> stations;
        try {
            stations = lastShellHttpService.getStations(sendLogDTO);
        } finally {
            long end = System.currentTimeMillis();
            sendLogDTO.setTimeConsumer(end - start);
        }

        if (null == stations || stations.isEmpty()) {
            return;
        }
//        else {
//            sendLogDTO.setResponseMsg(stations.toString());
//        }
        sync(stations, true);
        stations.clear();
    }

    @Transactional(rollbackFor = Exception.class)
    public void sync(List<GasStationDTO> stations, boolean isDelete) {
        final int size = stations.size();
        log.info("壳牌加油站数量: {}", size);
        if (stations.isEmpty()) {
            return;
        }
        List<CarParkGasStation> addGasStationList = new ArrayList<>(size);
        List<CarParkGasStation> updateGasStationList = new ArrayList<>(size);
        List<CarPark> addCarParkList = new ArrayList<>(size);
        List<CarPark> updateCarParkList = new ArrayList<>(size);
        List<CarParkLocation> addCarParkLocationList = new ArrayList<>(size);
        List<CarParkLocation> updateCarParkLocationList = new ArrayList<>(size);
        List<CarParkGasStationPrice> addGasPriceList = new ArrayList<>(size * 4);
        List<CarParkGasStationNozzle> addGasNozzleList = new ArrayList<>();
        List<CarParkImg> addCarParkImgList = new ArrayList<>(size);
        List<CarParkImg> updateCarParkImgList = new ArrayList<>(size);

        for (GasStationDTO station : stations) {
            String parkId = IdWorker.getIdStr();
            String companyId = buildCompanyId(station);
            if (!StringUtils.hasLength(companyId)) {
                log.error("转换companyId失败,brand: {}", station.getCompanyName());
                continue;
            }
            CarParkGasStation carParkGasStation = carParkGasStationService.getUniqueByGasStationCompanyNameAndStationId(companyId, String.valueOf(station.getId()));
            if (null == carParkGasStation) {
                carParkGasStation = getCarParkGasStation(station, parkId, companyId);
                addGasStationList.add(carParkGasStation);
            } else {
                parkId = carParkGasStation.getPId();
                CarParkGasStation newCarParkGasStation = getCarParkGasStation(station, parkId, companyId);
                updateGasStationList.add(newCarParkGasStation);
            }
            buildCarPark(addCarParkList, updateCarParkList, station, parkId);
            carParkGasStationPriceService.deleteCarParkGasStationPriceByParkId(parkId);
            carParkGasStationNozzleService.deleteCarParkGasStationNozzleByParkId(parkId);
            final List<GasPriceDTO> gasPrice = mergeToRepeat(station.getGun());
            for (GasPriceDTO gasPriceDTO : gasPrice) {
                buildCarParkGasStationPrice(addGasPriceList, parkId, gasPriceDTO, companyId);
                buildCarParkGasStationNozzle(addGasNozzleList, parkId, gasPriceDTO);
            }
            buildLocation(addCarParkLocationList, updateCarParkLocationList, station, parkId);
            buildImage(addCarParkImgList, updateCarParkImgList, station, parkId);
        }
        if (!addGasStationList.isEmpty()) {
            carParkGasStationService.saveBatch(addGasStationList);
            addGasStationList.clear();
        }
        if (!updateGasStationList.isEmpty()) {
            carParkGasStationService.updateBatchIds(updateGasStationList);
            updateGasStationList.clear();
        }
        if (!addCarParkList.isEmpty()) {
            carParkService.saveBatch(addCarParkList);
            addCarParkList.clear();
        }
        if (!updateCarParkList.isEmpty()) {
            carParkService.updateBatchIds(updateCarParkList);
            updateCarParkList.clear();
        }
        if (!addCarParkLocationList.isEmpty()) {
            carParkLocationService.saveBatch(addCarParkLocationList);
            addCarParkLocationList.clear();
        }
        if (!updateCarParkLocationList.isEmpty()) {
            carParkLocationService.updateBatchIds(updateCarParkLocationList);
            updateCarParkLocationList.clear();
        }
        if (!addGasPriceList.isEmpty()) {
            carParkGasStationPriceService.saveBatch(addGasPriceList);
            addGasPriceList.clear();
        }
        if (!addGasNozzleList.isEmpty()) {
            carParkGasStationNozzleService.saveBatch(addGasNozzleList);
            addGasNozzleList.clear();
        }
        if (!addCarParkImgList.isEmpty()) {
            carParkImgService.saveBatch(addCarParkImgList);
            addCarParkImgList.clear();
        }
        if (!updateCarParkImgList.isEmpty()) {
            carParkImgService.updateBatchIds(updateCarParkImgList);
            updateCarParkImgList.clear();
        }
        //删除不存在的加油站
        if (isDelete) {
            delete(stations);
        }
        final List<CarParkGasStation> carParkGasStations = getCarParkGasStations();
        if (!carParkGasStations.isEmpty()) {
            //加载到缓存中
            carParkGasStations.forEach(carParkGasStation -> carParkService.updateToRedis(carParkGasStation.getPId(), null, null));
        }
        stations.clear();
    }

    private static String buildCompanyId(GasStationDTO station) {
        String companyId;
        if ("延长壳牌".equals(station.getCompanyName())) {
            companyId = GasStationCompanyNameEnum.EXTEND_SHELL.getCode();
            station.setBrand(GasStationTypeEnum.SHELL.getCode());
        } else if ("延长壳牌高速".equals(station.getCompanyName())) {
            companyId = GasStationCompanyNameEnum.EXTEND_SHELL_OTHER.getCode();
            station.setBrand(GasStationTypeEnum.SHELL.getCode());
        } else {
            companyId = GasStationCompanyNameEnum.EXTEND_SHELL_OTHER.getCode();
            if (station.getName().contains("壳牌")) {
                station.setBrand(GasStationTypeEnum.SHELL.getCode());
            } else {
                station.setBrand(GasStationTypeEnum.GAS_TYPE_OTHER.getCode());
            }
        }
        return companyId;
    }

    private void buildImage(List<CarParkImg> addCarParkImgList, List<CarParkImg> updateCarParkImgList, GasStationDTO station, String parkId) {
        if (!StringUtils.hasLength(station.getLogo())) {
            return;
        }
        if (station.getBrand().equals(GasStationTypeEnum.SHELL.getCode())) {
            return;
        }
        URL url = URLUtil.url(station.getLogo());
        File file = new File(url.getFile());
        String id = station.getId() + "_" + "logo.jpg";
        CarParkImg carParkImg = carParkImgService.getById(id);
        file = new File(uploadDTO.getOfficialPath() + "shell", file.getName());
        if (!file.exists()) {
            try (InputStream stream = URLUtil.getStream(URLUtil.url(station.getLogo()))) {
                FileUtil.createFile(FileUtil.inputStreamToByte(stream), uploadDTO.getOfficialPath() + "shell", file.getName());
            } catch (IOException e) {
                throw new ApiException(e);
            }
        }
        String picPath = "shell" + File.separator + file.getName();
        if (null == carParkImg) {
            carParkImg = new CarParkImg();
            carParkImg.setPId(parkId);
            carParkImg.setId(id);
            carParkImg.setPicName("油站小图");
            carParkImg.setIdx(1);
            carParkImg.setPicPath(picPath);
            addCarParkImgList.add(carParkImg);
        } else {
            if (!picPath.equals(carParkImg.getPicPath())) {
                carParkImg.setPicPath(picPath);
                updateCarParkImgList.add(carParkImg);
            }
        }
    }

    private List<GasPriceDTO> mergeToRepeat(List<GasPriceDTO> gasPrice) {
        Map<Integer, GasPriceDTO> map = new HashMap<>();
        for (GasPriceDTO gasPriceDTO : gasPrice) {
            final GasPriceDTO mapGasPrice = map.get(gasPriceDTO.getOctaneNumber());
            if (null == mapGasPrice) {
                map.put(gasPriceDTO.getOctaneNumber(), gasPriceDTO);
            } else {
                if (gasPriceDTO.getPrice().equals(mapGasPrice.getPrice())) {
                    String nozzleNumbers = mapGasPrice.getNozzleNumbers();
                    if (!nozzleNumbers.endsWith(",")) {
                        nozzleNumbers += ",";
                    }
                    mapGasPrice.setNozzleNumbers(nozzleNumbers + gasPriceDTO.getNozzleNumbers());
                }
            }

        }
        map.forEach((key, value) -> {
            final String newNozzleNumbers = Arrays.stream(value.getNozzleNumbers().split(",")).distinct().collect(Collectors.joining(","));
            value.setNozzleNumbers(newNozzleNumbers);
        });
        final ArrayList<GasPriceDTO> list = new ArrayList<>(map.values());
        map.clear();
        return list;
    }


    private List<CarParkGasStation> getCarParkGasStations() {
        final QueryWrapper<CarParkGasStation> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("gas_station_company_name", GasStationCompanyNameEnum.EXTEND_SHELL.getCode(), GasStationCompanyNameEnum.EXTEND_SHELL_OTHER.getCode(), GasStationCompanyNameEnum.EXTEND_SHELL_HIGH.getCode());
        final List<CarParkGasStation> list = carParkGasStationService.getBaseMapper().selectList(queryWrapper);
        queryWrapper.clear();
        return list;
    }

    private void buildCarParkGasStationNozzle(List<CarParkGasStationNozzle> addGasNozzleList, String
            parkId, GasPriceDTO gasPriceDTO) {
        final String nozzleNumbers = gasPriceDTO.getNozzleNumbers();
        if (StringUtils.hasLength(nozzleNumbers)) {
            final Set<String> nozzleNumberSet = getNozzleNumberSet(nozzleNumbers);
            for (String s : nozzleNumberSet) {
                CarParkGasStationNozzle carParkGasStationNozzle = new CarParkGasStationNozzle();
                carParkGasStationNozzle.setNozzleNumber(s);
                carParkGasStationNozzle.setPId(parkId);
                carParkGasStationNozzle.setOctaneNumber(gasPriceDTO.getOctaneNumber());
                addGasNozzleList.add(carParkGasStationNozzle);
            }
        }
    }

    private Set<String> getNozzleNumberSet(String nozzleNumbers) {
        final String[] nozzleNumber = nozzleNumbers.split(",");
        //枪号可能重复，这里去重
        return Stream.of(nozzleNumber).collect(Collectors.toSet());
    }

    private void buildCarParkGasStationPrice(List<CarParkGasStationPrice> addGasPriceList, String
            parkId, GasPriceDTO gasPriceDTO, String companyId) {
        CarParkGasStationPrice carParkGasStationPrice = new CarParkGasStationPrice();
        final BigDecimal price = new BigDecimal(gasPriceDTO.getPrice());
        final BigDecimal stationPrice = new BigDecimal(gasPriceDTO.getPriceGun());
        final BigDecimal discountPrice = new BigDecimal(gasPriceDTO.getDiscount());
        carParkGasStationPrice.setPrice(price);
        carParkGasStationPrice.setStationPrice(stationPrice);

        //折扣和优惠价格这里不设置，因为折扣随时都在变化
        if (companyId.equals(GasStationCompanyNameEnum.EXTEND_SHELL.getCode())) {
            carParkGasStationPrice.setCustomerDiscount(100D);
            carParkGasStationPrice.setCustomerPrice(new BigDecimal(0));
        } else {
            carParkGasStationPrice.setCustomerDiscount(BigDecimalUtil.calculateDiscount(stationPrice, discountPrice).getCustomerDiscount().doubleValue());
            carParkGasStationPrice.setCustomerPrice(new BigDecimal(gasPriceDTO.getDiscount()));
        }
        carParkGasStationPrice.setOctaneNumber(gasPriceDTO.getOctaneNumber());
        carParkGasStationPrice.setPId(parkId);
        Set<String> nozzleNumberSet = getNozzleNumberSet(gasPriceDTO.getNozzleNumbers());
        ArrayList<String> nozzleNumberList = new ArrayList<>(nozzleNumberSet);
        SortUtil.sortNumericAlphabet(nozzleNumberList);
        StringBuilder str = new StringBuilder();
        for (String s : nozzleNumberList) {
            str.append(s).append(",");
        }
        carParkGasStationPrice.setNozzleNumbers(str.substring(0, str.length() - 1));
        addGasPriceList.add(carParkGasStationPrice);
    }

    private void buildCarPark(List<CarPark> addCarParkList, List<CarPark> updateCarParkList, GasStationDTO station, String parkId) {
        CarPark carPark = carParkService.getById(parkId);
        String enable = ConstantDto.SF1;
        if (null == carPark) {
            carPark = new CarPark();
            carPark.setPId(parkId);
            carPark.setName(station.getName());
            carPark.setParkNum(0);
            carPark.setIntoFreeTime(0);
            carPark.setPayFreeTime(0);
            carPark.setCarParkType(PandaConstant.CAR_PARK_TYPE_0);
            carPark.setAddTime(LocalDateTime.now());
            carPark.setThirdPartyBill(ConstantDto.SF1);
            carPark.setConfirmIn(ConstantDto.SF0);
            carPark.setConfirmOut(ConstantDto.SF0);
            carPark.setOperator(Constants.ADMIN_DEFAULT_ID);
            carPark.setOrgId(Constants.DEFAULT_TREE_PARENT_ID);
            carPark.setEnable(enable);
            carPark.setParkType(PandaConstant.PARK_TYPE_GAS_STATION);
            carPark.setOwnBusiness(ConstantDto.SF0);
            carPark.setOnStreet(ConstantDto.SF0);
            carPark.setParkSpaceType(PandaConstant.PARK_SPACE_TYPE2);
            carPark.setEtcPay(ConstantDto.SF0);
            final CarParkDTO carParkDTO = carParkService.modelToDto(carPark);
            parkHolderService.buildQrCode(carParkDTO);
            addCarParkList.add(carParkService.dtoToModel(carParkDTO));
        } else {
            if (!carPark.getEnable().equals(enable)) {
                carPark.setEnable(enable);
                updateCarParkList.add(carPark);
            }
        }
    }

    private void buildLocation(List<CarParkLocation> addCarParkLocationList, List<CarParkLocation> updateCarParkLocationList,
                               GasStationDTO station, String parkId) {
        CarParkLocation location = carParkLocationService.getById(parkId);
        final BigDecimal stationLat = new BigDecimal(station.getLatitude());
        final BigDecimal stationLng = new BigDecimal(station.getLongitude());
        if (null == location) {
            location = new CarParkLocation();
            addCarParkLocationList.add(location);
        } else {
//            if (location.getLat().equals(stationLat) && location.getLng().equals(stationLng)) {
//                return;
//            }
            updateCarParkLocationList.add(location);
        }
        location.setPId(parkId);
        location.setAddress(station.getAddress());
        location.setLat(stationLat);
        location.setLng(stationLng);
        location.setCityId(station.getCityCode());
        location.setCityName(station.getCityName());
        location.setProvinceId(station.getProvinceCode());
        location.setProvinceName(station.getProvinceName());
        final GeoPoint geoPoint = new GeoPoint();
        geoPoint.setLat(stationLat);
        geoPoint.setLng(stationLng);
        location.setLocation(geoPoint);
        location.setLocationType(5);
    }

    private CarParkGasStation getCarParkGasStation(GasStationDTO station, String parkId, String companyId) {
        CarParkGasStation carParkGasStation = new CarParkGasStation();
        carParkGasStation.setPId(parkId);
        carParkGasStation.setStationId(station.getId() + "");
        carParkGasStation.setGasStationCompanyName(companyId);
        carParkGasStation.setStationType(station.getBrand());
//        if ("SHELL".equals(station.getBrand())) {
//            carParkGasStation.setStationType(GasStationTypeEnum.SHELL.getCode());
//        } else if ("OTHER".equals(station.getBrand())) {
//            if (station.getName().contains("延长壳牌") && station.getName().contains("高速")) {
//                carParkGasStation.setStationType(GasStationTypeEnum.SHELL.getCode());
//            } else {
//                carParkGasStation.setStationType(GasStationTypeEnum.GAS_TYPE_OTHER.getCode());
//            }
//        } else if (("ZSY".equals(station.getBrand()))) {
//            carParkGasStation.setStationType(GasStationTypeEnum.PETRO_CHINA.getCode());
//        } else if (("ZSH".equals(station.getBrand()))) {
//            carParkGasStation.setStationType(GasStationTypeEnum.SINOPEC.getCode());
//        }

        final List<GasPriceDTO> gasPrice = station.getGun();
        if (!gasPrice.isEmpty()) {
            StringBuilder str = new StringBuilder();
            Set<String> set = new TreeSet<>();
            for (GasPriceDTO gasPriceDTO : gasPrice) {
                set.addAll(getNozzleNumberSet(gasPriceDTO.getNozzleNumbers()));
                if (!companyId.equals(GasStationCompanyNameEnum.EXTEND_SHELL.getCode())) {
                    try {
                        String stationPrice = gasPriceDTO.getPrice();
                        if (!StringUtils.hasLength(stationPrice)) {
                            stationPrice = "0";
                        }
                        String discountPrice = gasPriceDTO.getDiscount();
                        if (!StringUtils.hasLength(discountPrice)) {
                            discountPrice = "0";
                        }
                        gasPriceDTO.setCustomerDiscount(BigDecimalUtil.calculateDiscount(new BigDecimal(stationPrice), new BigDecimal(discountPrice)).getCustomerDiscount());
                    } catch (Exception e) {
                        log.error("parkId:{},stationId：{}", parkId, station.getId());
                    }
                }
            }
            if (companyId.equals(GasStationCompanyNameEnum.EXTEND_SHELL.getCode())) {
                carParkGasStation.setCustomerDiscount(DEFAULT_CUSTOMER_DISCOUNT);
            } else {
                //查询优惠最高的折扣,折扣值越小表示折扣越低
                AtomicReference<BigDecimal> customerDiscount = new AtomicReference<>();
                gasPrice.stream().map(GasPriceDTO::getCustomerDiscount).min(BigDecimal::compareTo).ifPresent(customerDiscount::set);
                carParkGasStation.setCustomerDiscount(customerDiscount.get().doubleValue());
            }
            final List<String> nozzleNumbers = new ArrayList<>(set);
            SortUtil.sortNumericAlphabet(nozzleNumbers);
            for (String nozzleNumber : nozzleNumbers) {
                str.append(nozzleNumber);
                str.append(",");
            }
            carParkGasStation.setNozzleNumbers(str.substring(0, str.length() - 1));
        }
        return carParkGasStation;
    }

    private void delete(List<GasStationDTO> stations) {
        final List<CarParkGasStation> list = getCarParkGasStations();
        if (!list.isEmpty()) {
            List<String> deleteParkIds = new ArrayList<>();
            final List<String> ids = stations.stream().map(gasStationDTO -> String.valueOf(gasStationDTO.getId())).collect(Collectors.toList());
            for (CarParkGasStation carParkGasStation : list) {
                if (!ids.contains(carParkGasStation.getStationId())) {
                    deleteParkIds.add(carParkGasStation.getPId());
                }
            }
            if (!deleteParkIds.isEmpty()) {
                log.info("壳牌,不存在的加油站,ids: {}", deleteParkIds);
                for (String parkId : deleteParkIds) {
                    removeByParkId(parkId);
                }
            }
        }
    }

    public void removeByStationIds(List<String> stationIds){
        for (String stationId : stationIds) {
            CarParkGasStation carParkGasStation = carParkGasStationService.getByStationId(stationId);
            if(null != carParkGasStation){
                removeByParkId(carParkGasStation.getPId());
                carParkGasStation.freeData();
            }
        }
    }

    private void removeByParkId(String parkId) {
        carParkService.removeById(parkId);
        carParkLocationService.removeById(parkId);
        carParkGasStationService.removeById(parkId);
        carParkGasStationPriceService.deleteCarParkGasStationPriceByParkId(parkId);
        carParkGasStationNozzleService.deleteCarParkGasStationNozzleByParkId(parkId);
        carParkImgService.deleteCarParkImgByParkId(parkId);
    }
}